/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Communication;

import Request.RequestRdyArg;
import Common.InfoLogger;
import Response.*;
import Request.*;
import java.io.*;
import java.util.*;
import java.net.*;

import AccManager.ServerAccManager;
import AccManager.*;

//Timer listener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import Events.*;

/**
 *
 * @author lcy
 */
public class GameServer implements RequestReadyHandler, ActionListener,ProbeRespHandler{

    private ServerTransceiver _server_transceiver;
    private ServerAccManager database_manager;
    private InfoLogger _debugger;
    private ProjTimer _timer;
    private volatile int _logCounter;
    private int _timerPeriod;

    public GameServer(){
        _server_transceiver = new ServerTransceiver();

        InitCommComponents();
    }

    public GameServer(int port){
        
        _server_transceiver = new ServerTransceiver(port);
        InitCommComponents();
    }

    void InitCommComponents(){

        database_manager = new ServerAccManager("UsrList.xml");
        _debugger = new InfoLogger();

        _timerPeriod = 6000;
        _timer = new ProjTimer(_timerPeriod,this);
        _logCounter = 0;
    }

    public void StartServer(){
        /* Read all database entries into memory */
        database_manager.ParseCurDataBase();

        database_manager.NewEmptyDBFile();

        database_manager.InitImport();

        /* When the transceiver needs further help,
           the game server is informed.
         */
        _server_transceiver.addRequestReadyHandler(this);

        _timer.start();

        //Start running the transceiver;
        _server_transceiver.OpenTransceiver();

        //TODO: register the Game Server to probe
        //response event;
        _server_transceiver.addProbeRespHandler(this);
        
    }
    
    /*****************************************************
     * Function: WaitingForRequest()
     * Summary: Wait for new user request
     * Input:
     * Returns: null if any exception occurs
     * Side effects:
     * Pre-Conditions:
    ******************************************************/

    public Socket WaitingForRequest(){
        
        Socket new_request;

        /* Listen for new connection*/
        try{
            new_request = _server_transceiver.ListenForRequest();
            _debugger.LogInfo("Game Server: Waiting for request ...");
        }catch(SocketTimeoutException ex){
            _debugger.LogInfo("Game Server: Server socket time out");
            return null;
        }

        return new_request;
    }

    public void ProcessNewClientRequest(Socket new_request){

        _server_transceiver.ProcessNewRequest(new_request);
    }

    /*Handle users' requests*/
    public synchronized void HandleResponse(RequestResultReadyEvent event){
        
        RequestRdyArg arg = event.GetArg();

        NetDataResponse response = arg.GetResponse();

        Request request = response.GetRequest();

        switch(request.GetRequestType()){
            case REGISTRATION:
                HandleRegistration(arg);
                break;
            case LOGIN:
                HandleLogin(arg);
                break;
            case USERLIST:
                HandleUserListRequest(arg);
                break;
            case GENERAL:
                HandleGeneral(arg);
                break;
            default:
                break;
        }
        
    }

    private void HandleGeneral(RequestRdyArg arg){
        NetDataLRResp response = (NetDataLRResp)arg.GetResponse();
        GeneralRequest request = (GeneralRequest)(response.GetRequest());
    }

    private void HandleRegistration(RequestRdyArg arg){

        NetDataLRResp response = (NetDataLRResp)arg.GetResponse();
        RegisterRequest request = (RegisterRequest)(response.GetRequest());

        UserProfile new_user = new UserProfile(
                (UserProfile)request.GetRequestContent());

        if(database_manager.IsNewUserValid(new_user)){
            database_manager.AddNewUser(new_user);
            database_manager.FillInDBFile();
            _server_transceiver.AddValidateUserMsg(response, UserValidResult.VALID);
        }
        else{

            _server_transceiver.AddValidateUserMsg(response, UserValidResult.EXIST);
        }

        SendRespArg resp_arg = new SendRespArg(arg.GetRequestId(),response);

        SendRespEvent send_event = new SendRespEvent(_server_transceiver,resp_arg);

        _server_transceiver.fireSendRespEvent(send_event);
    }

    private void HandleLogin(RequestRdyArg arg){
        int idx;

        NetDataLRResp response = (NetDataLRResp)arg.GetResponse();
        LoginRequest request = (LoginRequest)response.GetRequest();
        
        UserProfile login_user = new UserProfile(
                (UserProfile)request.GetRequestContent());

        //If current user exists, then do password checking;
        if((idx = database_manager.FindUser(login_user)) >= 0){
            if(database_manager.UserLoginAuthentication(idx, login_user)){
               
                _server_transceiver.AddValidateUserMsg(response, UserValidResult.VALID);
            }
            else
                _server_transceiver.AddValidateUserMsg(
                        response, UserValidResult.PASSWD_MISMATCH);
        }
        else{
             _server_transceiver.AddValidateUserMsg(
                     response, UserValidResult.NON_EXIST);
        }
        
        SendRespArg resp_arg = new SendRespArg(arg.GetRequestId(),response);

        SendRespEvent send_event = new SendRespEvent(_server_transceiver,resp_arg);

        _server_transceiver.fireSendRespEvent(send_event);
    }

    private void HandleUserListRequest(RequestRdyArg arg){
        int usr_idx =0;
        _debugger.LogInfo("GameServer: Handle User List");
        
        NetDataUserListResp response = (NetDataUserListResp)arg.GetResponse();
        UserListRequest request = (UserListRequest)response.GetRequest();

        String name = (String) request.GetRequestContent();

        //Password and status will NOT be used
        UserProfile request_user = new UserProfile(name,"");

        //First check if the requesting user is online;
        UserStatus status = database_manager.IsUserOnline(request_user);

        switch(status){
            //The user is still online and we can send the list back to him/her
            case ONLINE:
                try{              
                    usr_idx = database_manager.FindUser(request_user);
                    UserProfile profile = database_manager.getUserProfile(usr_idx); 

                    response.CreateReturnList(profile, database_manager.GetUserListRef());
                    
                    response.SetResultMsg(
                            ResponseMsgList.msg_list[ResponseMsgList.VALID_IDX]);
                    
                }catch(NullPointerException ex){
                    _debugger.LogInfo(ex.getMessage());
                }
                break;

            case OFFLINE:
                response.SetResultMsg(
                        ResponseMsgList.msg_list[ResponseMsgList.OFFLINE_IDX]);
                break;

            case NONEXIST:
                _server_transceiver.AddValidateUserMsg(
                     response, UserValidResult.NON_EXIST);
                break;

            default:
                break;
        }

        SendRespArg resp_arg = new SendRespArg(arg.GetRequestId(),response);

        SendRespEvent send_event = new SendRespEvent(_server_transceiver,resp_arg);

        _server_transceiver.fireSendRespEvent(send_event);
    }

    //For timer
    public void actionPerformed(ActionEvent evt)
    {
        //TODO: update user time to live;
        database_manager.DecrementTTL();
        ProbeEvent eve = new ProbeEvent(this,null);
        _server_transceiver.fireProbeSocketEvent(eve);

        _logCounter ++;
        if(_logCounter == 10){
            database_manager.FillInDBFile();
            _logCounter = 0;
        }
    }

    //When Probe response event occurs
    public void HandleProbeResp(ProbeRespReceived eve){
        ProbeRespArg arg = eve.GetArg();

        String usr_name = arg.GetUserName();
        database_manager.ResetTTL(usr_name);
    }
}
